<?php
/**
 * Descripcio de ModelPersistent
 * Classe abstracta que asegura la persistencia d'un model de dades,
 * assegurant si cal, el manteniment de les taules d'histórics.
 * Obtè els noms dels camps directament de la base de dades,
 * facilitant la modificació de les plantilles de presentació
 *
 * @version 1.14 -- 201205242300
 * @author jferrert@copernic.cat  18/04/2012
 * @copyright Creative commons (by-nc-nd) Reconeixement - NoComercial - CompartirIgual 
 */

require_once 'Db.class.php';
require_once 'Conf.class.php';

abstract class ModelPersistent {
	private $_db;          // Enllaç a la connexió de la base de dades
	private $_taula;       // Nom de la taula principal
	private $_historic;    // Cert si calen d'histórics
	private $_taula_hst;   // Nom de la taula d'histórics
	private $_fila;        // Contingut de la fila actual
	private $_data;        // Array associatiu amb tots els camps de la taula buits
	private $_camps;       // Llista dels camps de la taula
	private $_clau;        // Nom del camp que es index primari, si no s'especifica es "id"
	private $_esAuto;      // Si el camp index primari es autonuméric, si no s'especifica es cert. 

   /**
    * Constructor
    * @param String $taula Nom de la taula corresponent de la base de dades
    * @param bool   $historic  true/false si ha de tenir taula d'histórics (def false)
    * @param String $clau      Nom del camp clau si no es "id" (def "id") 
    * @param bool   $auto      true/false Si el camp clau es o no autonuméric (def true)
    */
	public function __construct($taula, $historic = false, $clau="id", $auto=true){
		$this->_db        = Db::getInstance();
		$config           = Conf::getInstance();
		$this->_taula     = $config->getPrefixTaules().$taula;
		$this->_historic  = $historic;
		$this->_taula_hst = $historic ? $this->_taula.$config->getSufixHistoric() :'';
		$this->_fila      = Array();
		$this->_clau      = $clau;
		$this->_esAuto    = $auto;
		$this->getData();     // recupera la informació per $_data i $_camps de la base de dades
	}

   /**
    * Retorna un array associatiu d'una fila de la base de dades amb els camps en blanc
    * @return <type> 
    */
    public function newRecord(){
       return $this->_data;
    }

    /**
     * Retorna en un array associatiu la fila amb l'id especificat.
     * Desa una cópia a $_fila i si cal un altre a $_SESSION[$_taula]
     * @param <type> $id
     * @param <type> $reserva, si cal desar el registre també a la sessió.
     * @param <type> $strip, si cal eliminar els camps de sistema.
     * @return <type>
     */
    public function getRecord($id = 0, $reserva = true, $strip = true){
	   $id = htmlentities($id);
       $sql = "SELECT $this->_camps FROM $this->_taula WHERE  $this->_clau = \"$id\"";
       $this->_fila = $this->_db->getFila($this->_db->executarQuery($sql));
	   // Es desa a la sessió el registre llegit (suposadament per fer edit o show)
       if($reserva) $_SESSION[$this->_taula] = $this->_fila;
       // Eliminar els camps de sistema (privats)
       if($strip)   $this->_fila = $this->stripSystemFields($this->_fila);
       return $this->decodeData($this->_fila);
    }

    /**
     * Retorna en un array associatiu la primera fila de la sentència SQL.
     * @param <type> $sql sentència SQL
     * @return <type>
     */
    public function getRecordSQL($sql){
		return $this->decodeData($this->_db->getFila($this->_db->executarQuery($sql)));
    }

    /**
     * Retorna un array associatiu amb les files que compleixen amb
     * les condicions especificades
     * @param <type> $where   Condició SQL per WHERE
     * @param <type> $primer  Quin es el primer registre a retornar
     * @param <type> $quants  Quants registres ha de tornar com a màxim
     * @return <type>
     */
    public function listRecords($where=null, $primer=null, $quants=null){
		$sql  = "SELECT $this->_camps FROM $this->_taula ";
		$sql .= isset($where) ? " WHERE ".$where : "";
		$sql .= isset($quants) && !isset($primer) ? " LIMIT ". $quants : "";
		$sql .= isset($quants) &&  isset($primer) ? " LIMIT ". $primer.", ".$quants : "";
		$taula = $this->_db->getLlista($this->_db->executarQuery($sql));
		for($i = 0; $i < count($taula); $i++){ 
			$taula[$i] = $this->decodeData($this->stripSystemFields($taula[$i]));
		}
		return $taula; 
    }

    /**
     * Retorna un array associatiu amb les files de la sentència SQL.
     * @param <type> $sql sentència SQL
     * @return <type>
     */
    public function listRecordsSQL($sql){
		$taula = $this->_db->getLlista($this->_db->executarQuery($sql));
		for($i = 0; $i < count($taula); $i++){ 
			$taula[$i] = $this->decodeData($this->stripSystemFields($taula[$i]));
		}
		return $taula; 
    }

    /**
     * Insereix una fila amb les dades passades a $data
     * @param <type> $data         Array associatiu amb les dades per inserir.
     * @param <type> $llistaCamps  String amb una llista amb els noms dels camps.
     * @param <type> $llistaValors String amb una llista amb les noves dades.
     * @return clau                id de l'última inseció.
     */
    public function insertRecord($data, $llistaCamps = "", $llistaValors = ""){
		$num = 0;
		$data = $this->encodeData($data);
		// Hi ha dades i o bè es automàtic o te contingut en el camp clau   
		if(isset($data) && ($this->_esAuto || (isset($data[$this->_clau]) && $data[$this->_clau] != ""))) {
            foreach($data as $key => $valor){
				// Si es el camp clau posar-hi el valor o be nul si es automàtic (si no hi es, no s'hi posa)
				if($this->_clau == $key){
					$llistaCamps  .= ($llistaCamps  == "" ? "" : ", ").$key;  
                    $llistaValors .= ($llistaValors == "" ? "" : ", ").($this->_esAuto ? 'null' : $valor);				
				} else {
					// Si no afegir el camp si existeix al registre, te valor i no es camp de seguretat.
					if(isset($this->_data[$key]) && $valor != "" && 
					         $key !='creat'     && $key !='usercrea'  && 
					         $key !='modificat' && $key !='usermodif'){
						$llistaCamps  .= ($llistaCamps  == "" ? "" : ", ").$key;  
						$llistaValors .= ($llistaValors == "" ? "" : ", ")."\"".$valor."\"";
					}
				}
            }
            if(strlen($llistaCamps) > 0){
                $llistaCamps  .= ", creat, usercrea";        
                $llistaValors .= ", now(), ". (isset($_SESSION["login"]["id"]) ? $_SESSION["login"]["id"]:"1");
                $sql = "INSERT INTO $this->_taula($llistaCamps) VALUES($llistaValors)";
                if($this->_esAuto){
					$num = $this->_db->inserir($sql);
				} else {
					$num = $this->_db->executar($sql) ? $data[$this->_clau] : 0;
				}
			}
		}
		return $num;
    }

    /**
     * Actualitza la fila actual amb les noves dades si aquestes han canviat respecte les originals.
     * Si cal actualitza l'históric.
     * @param <type> $data Array associatiu amb les noves dades
     * @param $llistaSet   Si es vol forçar algun(s) camp(s) (ex : "nom = \"Perico\", edat = 3 " )
     * @return <type>
     */
	public function updateRecord($data, $llistaSet = ""){
		$num  = 0;
		$data = $this->encodeData($data);
		// Hi ha dades i te el camp clau i aquest té contingut  
		if(isset($data) && isset($data[$this->_clau]) && ($this->_esAuto ? ($data[$this->_clau] > 0) : ($data[$this->_clau] != ""))) {
			$id = $data[$this->_clau];
			// Obtenir la hora i data de la última lectura del registre desat a la sessió
			$old_modificat = isset($_SESSION[$this->_taula]['modificat']) ? $_SESSION[$this->_taula]['modificat'] :'';
			// Llegir de la base de dades el registre que es pretèn modificar
			$oldRecord = $this->getRecord($id,false,false);
			// Verificar que no s'ha modificat des-de la última lectura
			if(isset($oldRecord['modificat']) && $old_modificat == $oldRecord['modificat']){
			//if(isset($oldRecord['modificat'])){
				foreach($data as $key => $valor){
					if( $key != $this->_clau && $key !='creat'     &&
						$key !='usercrea'    && $key !='modificat' &&
						$key !='usermodif'   && isset($this->_data[$key]) && $oldRecord[$key] != $data[$key]) {
						$llistaSet .= ($llistaSet == "" ? "" : ", ").$key."=". "\"".$valor."\"";
					}
				}
				if(strlen($llistaSet) > 0){
					if($this->_historic){
						$llistaCamps  = "usr_, ip_, accio_";
						// Dades en la sessió
						if(isset($_SESSION['login'])) $llistaValors .= "\"".$_SESSION['login']['id']."\",\"".$_SESSION['login']['IP']."\",\"U\"";
						else $llistaValors = "1, \"000.000.000.000.000.000\", \"U\"";
						foreach($oldRecord as $key => $valor){
							$llistaCamps .= ", ".$key."";  $llistaValors .= ", \"".$valor."\"";
						}
						$sql = "INSERT INTO $this->_taula_hst($llistaCamps) VALUES($llistaValors) ";
						$this->_db->executar($sql);
					}
					// Dades en la sessió
					$llistaSet .= ", modificat = now(), usermodif = \"".(isset($_SESSION["login"]["id"]) ? $_SESSION["login"]["id"]:"1")."\"";
					$sql = "UPDATE $this->_taula SET $llistaSet WHERE $this->_clau = $id";
					return $this->_db->executar($sql);
				}
			}
		}
		return 0;
	}

    /**
     * Elimina la fila indicada a id. Si cal actualitza l'históric.
     * @param <type> $id   Valor per el camp clau
     * @return <type>
     */
     public function deleteRecord($id=0){
		$id = htmlentities($id);
		 // Tenim camp clau amb contingut
		if(($this->_esAuto ? $id > 0 : $id != "")){
			// Llegim si esisteix el registre
			$oldRecord = $this->getRecord($id);
			// Si existeix cal eliminar-lo
			if(isset($oldRecord[$this->_clau])){
				// Si la taula té históric desem l'antic
				if($this->_historic){
					$llistaCamps  = "usr_, ip_, accio_";
					// Dades en la sessió
					if(isset($_SESSION['login'])) $llistaValors .= "\"".$_SESSION['login']['id']."\",\"".$_SESSION['login']['IP']."\",\"D\"";
					else $llistaValors = "1, \"000.000.000.000.000.000\", \"D\"";
					foreach($oldRecord as $key => $valor){
						$llistaCamps .= ", ".$key."";  $llistaValors .= ", \"".$valor."\"";
					}
					$sql = "INSERT INTO $this->_taula_hst($llistaCamps) VALUES($llistaValors) ";
					$this->_db->executar($sql);
				}
				// Esborrem el registre
				$sql = "DELETE FROM $this->_taula WHERE $this->_clau = $id";
				return $this->_db->executar($sql);
			}
		}
		return 0;
    }
    
    /**
     * Elimina els camps indicats d'un array associatiu
     */
	public function stripFields($data, $camps){
		$newData = Array();
		foreach($data as $clau => $valor) {
			if(!strpos(" ".$camps,$clau)){
					$newData[$clau] = $valor;
			}
		}
		return $newData;
	}    
 
    /**
     * Elimina els camps de sistema d'un array associatiu
     */
	public function stripSystemFields($data){
		return $this->stripFields($data,"creat,usercrea,modificat,usermodif");
	}    

    /**
     * campClau
     * Retorna el nom del camp clau 
     */
    public function campClau(){
        return $this->_clau;
    }

    /**
     * esAuto
     * Retorna si el camp clau es automàtic 
     */
    public function esAuto(){
        return $this->_esAuto;
    }

	/**
	 * toString
	 */
    public function toString(){
        return "Model Persistent : taula = $this->_taula";
    }

   //*************  funcions privades
    /**
     * Inicialitza la llista de camps i un array associatiu amb tots els camps sense contingut 
     */
	private function getData(){
		$this->_data = Array();
		$this->_camps  = "";
		$result = $this->_db->getLlista($this->_db->executarQuery("DESCRIBE $this->_taula"));
		foreach($result as $fila){
			if($fila['Field'] != 'creat'     && $fila['Field'] !='usercrea' &&
			   $fila['Field'] != 'modificat' && $fila['Field'] !='usermodif'){
					$this->_data[$fila['Field']] = "";
			}
			$this->_camps .= ($this->_camps == "" ? "" : ", ").$fila['Field'];
		}
	}       
   
    /**
     * Converteix els caràcters de tots els camps a entitats HTML
     */
	private function encodeData($data){
		$retorn = Array();
		foreach($data as $clau => $valor) $retorn[$clau] = htmlentities($valor); 
		return $retorn;
	}    

    /**
     * Reconverteix els caràcters entitats HTML a la seva forma inicial
     */
	private function decodeData($data){
		$retorn = Array();
		foreach($data as $clau => $valor) $retorn[$clau] = html_entity_decode($valor); 
		return $retorn;
	}    
}
?>
